深度学习算法(第26期)----深度网络中的自编码器
上期我们一起学习了机器翻译中的编码解码器网络的相关知识,
深度学习算法(第25期)----机器翻译中的编码解码器网络
今天我们一起学一下深度网络中的自编码器.
自编码器是在无监督(训练集未标注)的情况下,能够学习有效表示数据(称为编码)的一种深度人工网络。这些编码一般跟输入数据比起来有更低的维度,这使得自编码器在数据降维方面比较有用。更重要的是,自编码器可以作为强大的特征检测器,它可以在深度网络中用于无监督的预训练。最后,它可以随机产生和训练数据相似的新数据,这叫做生成模型。例如,我们可以训练一个人脸图像上的自编码器,那么它能够产生新的人脸图像。
咋一看,自编码器的工作就是学会简单的把输入值copy到输出,然而,训练网络的时候,可以从各个角度进行约束网络,使它不能够做简单的复制,进而能够训练出有效的网络。比方说,我们可以限制内部呈现的大小,或者在输入图像上加些噪声来训练网络恢复原始输入。这些约束可以防止网络玩障眼法,或者说防止网络去简单的复制输入到输出。这就强制它去学习表示数据的有效方法。简而言之,编码是自编码器在一些约束下尝试学习恒等方程而得到的副产品。
接下来,我们将更进一步的学习自编码器的工作原理,不管是在降维,特征提取,无监督预训练,还是生成模型,以及可以实施哪些约束,以及如何在tensorflow中实现。
有效的数据表示
我们来看两组数字,下面哪一组数更好记呢?
• 40, 27, 25, 36, 81, 57, 10, 73, 19, 68
• 50, 25, 76, 38, 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20
咋一看,感觉应该是第一组数据更好记一些,因为第一组短一点嘛。然而仔细看第二组数据就会发现,这条数据是有规律的,即:如果是偶数的话,那么它后面跟的是该偶数的一半,如果是奇数的话,后面跟的是该数的3倍加1(其实该序列为希尔顿序列)。一旦我们发现了这个规律,那么第二组数据就更为好记了。
关于记忆,感知和模式匹配的关系在 20 世纪 70 年代早期由 William Chase 和 Herbert Simon 研究。他们注意到,专业的棋手能够通过观察棋盘5秒钟就记住所有棋子的位置,这个任务,大多数人都会认为不可思议。然而也只是在正常比赛中才会是这种情况,而不是随机放置位置。这也就说明了,专业棋手并不是记忆力比我们好很多,而是由于丰富的经验,他们更看到棋子背后的模式,也就是说模式能够帮他们更为高效的存储信息。
就像上面说的专业棋手一样,自编码器将输入转换成一种高效的内部模式呈现,并且输出一些跟输入很似接近的东西。一个自编码器通常由两部分组成:编码(识别网络)用来将输入转换成一个内部表示,解码(生成网络)用来将内部表示转换为输出,如下图:
正如我们上图看到的一样,除了自编码器的输入输出必须一致以外,一个自编码器基本上跟多层感知机(MLP)有着相同的架构。在上面的例子中,仅仅有一个包含两个神经元的隐藏层(编码),和一个由三个神经元组成的输出层(解码)。由于自编码器在尽力的恢复输入数据,所以输出通常也被称为重构。损失函数为重构损失,往往会在跟输入不一致的时候惩罚模型。
由于这个内部呈现跟输入数据比起来维度更低,所以自编码器被认为是不完整的,一个不完整的自编码器不能简单的复制输入到编码,也就是说,它必须找到一个模式来输出输入的近似。这就强制它去学习输入数据的一些重要的特征,而抛弃不重要的特征。
现在,我们一起看一下,如何用不完整的自编码器来实现降维的。
用不完整的线性编码器实现PCA
如果自编码器仅仅使用线性激活函数,并且用MSE作为损失函数的话,则它将最终收敛为我们之前学过的PCA(主成分分析)模型(见下链接)。
机器学习三人行(系列十)----机器学习降压神器(附代码)
我们一起看下如何创建一个简单的线性自编码器来实现PCA,将一个三维数据映射到两维:
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
n_inputs = 3 # 3D inputs
n_hidden = 2 # 2D codings
n_outputs = n_inputs
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
hidden = fully_connected(X, n_hidden, activation_fn=None)
outputs = fully_connected(hidden, n_outputs, activation_fn=None)
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(reconstruction_loss)
init = tf.global_variables_initializer()
这段代码跟我们之前学的MLP基本一致,需要注意的是:
神经元的输入数量和输出数量一致。
为了实现PCA,这里设置activation_fn=None,并且损失函数为MSE。
现在我们将加载数据,在训练集上训练模型,并且用模型去编码测试数据(投影到2D):
X_train, X_test = [...] # load the dataset
n_iterations = 1000
codings = hidden # the output of the hidden layer provides the codings
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
training_op.run(feed_dict={X: X_train}) # no labels (unsupervised)
codings_val = codings.eval(feed_dict={X: X_test})
下图显示了原始数据(左侧),和自编码器的隐藏层的输出(编码层,右图),正如我们看到的,自编码器找到了将数据投影到数据上的最佳二维平面,保留了数据的尽可能多的差异(就像 PCA 一样)。
好了,至此,今天我们简单学习了深度网络中的自编码器的相关知识,希望有些收获,下期我们将更深一步的学习下堆叠的自编码器及其相关实现的相关知识,欢迎留言或进社区共同交流,喜欢的话,就点个“在看”吧,您也可以置顶公众号,第一时间接收最新内容。